/*

Summary:
A base class of those classes: one object owns one thread. 
It simplifies thread control and inter-thread communication.

Remark:
All methods, except Begin, are available only after Begin 
is called. And once End is called, these methods become 
unavailable, except Wait.

Begin has two overloads, the one that has no parameter calls the 
other with argument false. The parameter means that if calling
thread would block until the new thread finish its basic 
initialization, so that calling thread can call other methods
immediately after Begin returns. 

Sub classes just need to override MsgProc if they only use 
window messge to work, and of course they can also override
Startup and Dispose to finish some initialization and cleanup.
Otherwise, sub classes can override DoWork to do special 
things. In this case, Startup, MsgProc and Dispose will not 
be called unless they were did in DoWork.

If Startup return false, the thread will not continue and
exit at once, Dispose will even not called.

Version:
0.2.1

LastUpdatedDate:
2012-09-06

*/

#pragma once

#include <crtdbg.h>
#include <process.h>
#include <Windows.h>

namespace Zaf {
namespace Sync {


class ThreadObject {

public:
	ThreadObject();
	virtual ~ThreadObject();

	HWND GetWindowHandle() const;

	void Begin();
	void Begin(bool waitReady);
	void End();

	bool Wait();
	bool Wait(DWORD time);

	void Terminate();

	void PostMsg(DWORD msg);
	void PostMsg(DWORD msg, WPARAM wParam, LPARAM lParam);

protected:
	bool GetMsg(MSG& msg);
	bool PeekMsg(bool removeMsg, MSG& msg);

private:
	static unsigned int __stdcall ThreadEntry(void*);

	ThreadObject(const ThreadObject&);
	const ThreadObject& operator=(const ThreadObject&);

	virtual void DoWork();

	virtual bool Startup();
	virtual void MsgProc(DWORD msg, WPARAM wParam, LPARAM lParam);
	virtual void Dispose();

	void OnThreadEnd();

	bool m_hasStarted;
	HANDLE m_hThread;
	HANDLE m_readyEvent;
	HWND m_hwnd;
};



inline ThreadObject::ThreadObject() : m_hThread(NULL), m_readyEvent(NULL), m_hwnd(NULL), m_hasStarted(false) { }

inline ThreadObject::~ThreadObject() {

	if (m_hThread != NULL) {
		CloseHandle(m_hThread);
	}

	if (m_hwnd != NULL) {
		DestroyWindow(m_hwnd);
	}
}

inline HWND ThreadObject::GetWindowHandle() const {
	return m_hwnd;
}

inline void ThreadObject::Begin() {
	Begin(false);
}

inline void ThreadObject::End() {
	PostMsg(WM_QUIT);
}

inline bool ThreadObject::Wait() {
	return Wait(INFINITE);
}

inline bool ThreadObject::Wait(DWORD time) {
	return (WaitForSingleObject(m_hThread, time) == WAIT_OBJECT_0);
}

inline void ThreadObject::Terminate() {
	TerminateThread(m_hThread, -1);
	OnThreadEnd();
}

inline void ThreadObject::PostMsg(DWORD msg) {
	PostMsg(msg, 0, 0);
}

inline void ThreadObject::PostMsg(DWORD msg, WPARAM wParam, LPARAM lParam) {
	PostMessage(m_hwnd, msg, wParam, lParam);
}

inline bool ThreadObject::GetMsg(MSG& msg) {
	return GetMessage(&msg, NULL, 0, 0) != 0;
}

inline bool ThreadObject::PeekMsg(bool removeMsg, MSG& msg) {
	return PeekMessage(&msg, NULL, 0, 0, removeMsg) != 0;
}

inline bool ThreadObject::Startup() { 
	return true;
}

inline void ThreadObject::MsgProc(DWORD msg, WPARAM wParam, LPARAM lParam) {

}

inline void ThreadObject::Dispose() {

}

}
}